package com.yulian.settlementcenter;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;

import com.yulian.service.BatchImport;
import com.yulian.util.MyDesUtil;
import com.yulian.web.handler.CommonHandler;
import com.yulian.web.handler.ReportCommonHandler;
import com.yulian.web.handler.impl.ReportCommonHandlerImpl;
import com.yulian.web_api.system_management.UserManager;

import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.Log4JLoggerFactory;
import io.vertx.config.ConfigRetriever;
import io.vertx.config.ConfigRetrieverOptions;
import io.vertx.config.ConfigStoreOptions;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.DeploymentOptions;
import io.vertx.core.Future;
import io.vertx.core.Promise;
import io.vertx.core.Vertx;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.eventbus.EventBus;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.auth.PubSecKeyOptions;
import io.vertx.ext.auth.jwt.JWTAuth;
import io.vertx.ext.auth.jwt.JWTAuthOptions;
import io.vertx.ext.web.FileUpload;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.client.WebClient;
import io.vertx.ext.web.handler.BodyHandler;
import io.vertx.ext.web.handler.CorsHandler;
import io.vertx.ext.web.handler.JWTAuthHandler;
import io.vertx.ext.web.handler.StaticHandler;
import io.vertx.pgclient.PgConnectOptions;
import io.vertx.pgclient.PgPool;
import io.vertx.redis.client.Redis;
import io.vertx.sqlclient.PoolOptions;
import web.report.handler.*;

/**
 * Verticle类，项目启动入口，主要完成：
 * 读取配置文件，创建httpServer，创建路由管理对象router，创建数据连接池对象，创建事件总线eventBus，创建redis，创建webClient，
 * 进行对外数据接口的路由管理，JWT权限控制等
 *
 * @author
 */
public class MainVerticle extends AbstractVerticle {

    final InternalLogger logger = Log4JLoggerFactory.getInstance(MainVerticle.class);
    //声明 Router对象
    Router router;
    //声明 连接选项对象
    PgConnectOptions connectOptions;
    //声明 连接池选项对象 poolOptions
    PoolOptions poolOptions;
    //声明 连接池对象 pool
    PgPool pool;
    //redis 变量
    Redis redis;
    //HTTP参数配置对象
    Set<String> allowedHeaders = new HashSet<String>();
    Set<HttpMethod> allowedMethods = new HashSet<HttpMethod>();

    /**
     * 忽略非法反射警告  适用于jdk11
     */
    @SuppressWarnings("unchecked")
    public static void disableAccessWarnings() {
        try {
            @SuppressWarnings("rawtypes")
            Class unsafeClass = Class.forName("sun.misc.Unsafe");
            Field field = unsafeClass.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            Object unsafe = field.get(null);

            Method putObjectVolatile = unsafeClass.getDeclaredMethod("putObjectVolatile", Object.class, long.class, Object.class);
            Method staticFieldOffset = unsafeClass.getDeclaredMethod("staticFieldOffset", Field.class);

            @SuppressWarnings("rawtypes")
            Class loggerClass = Class.forName("jdk.internal.module.IllegalAccessLogger");
            Field loggerField = loggerClass.getDeclaredField("logger");
            Long offset = (Long) staticFieldOffset.invoke(unsafe, loggerField);
            putObjectVolatile.invoke(unsafe, loggerClass, offset, null);
        } catch (Exception ignored) {
        }
    }

    public static void main(String[] args) {

        disableAccessWarnings();
        //int worker_pool_size =  Runtime.getRuntime().availableProcessors() * 2;
        DeploymentOptions mainDeploymentOptions = new DeploymentOptions();
        mainDeploymentOptions.setInstances(1);
        Vertx vertx = Vertx.vertx();
        vertx.deployVerticle("com.yulian.settlementcenter.MainVerticle", mainDeploymentOptions, res -> {
            if (res.succeeded()) {
                String deploymentID = res.result();
                System.out.println("settlementcenter.MainVerticle deployed ok, deploymentID = " + deploymentID);


            } else {
                res.cause().printStackTrace();
            }
        });
    }


    @Override
    public void start(Promise<Void> startPromise) throws Exception {
        //在allowedHeaders中添加内容
        allowedHeaders.add("x-requested-with");
        allowedHeaders.add("Access-Control-Allow-Origin");
        allowedHeaders.add("Access-Control-Allow-Headers");
        allowedHeaders.add("origin");
        allowedHeaders.add("Authorization");
        allowedHeaders.add("Content-Type");
        allowedHeaders.add("Accept");
        allowedHeaders.add("X-PINGARUNER");
        //在allowedMethods中添加内容
        allowedMethods.add(HttpMethod.GET);
        allowedMethods.add(HttpMethod.POST);
        allowedMethods.add(HttpMethod.OPTIONS);
        allowedMethods.add(HttpMethod.DELETE);
        allowedMethods.add(HttpMethod.PATCH);
        allowedMethods.add(HttpMethod.PUT);

        // 此配置仓库仅仅从文件中读取配置，支持所有的格式，默认是json，可以是properties格式, yaml格式
        ConfigStoreOptions fileStore1 = new ConfigStoreOptions()
        .setType("file")
        .setConfig(new JsonObject().put("path", "conf/config.json"));
        ConfigStoreOptions fileStore2 = new ConfigStoreOptions()
        .setType("file")
        .setFormat("yaml")
        .setOptional(true)
        .setConfig(new JsonObject().put("path", "conf/common-constant.yaml"));
        ConfigStoreOptions fileStore3 = new ConfigStoreOptions()
        .setType("file")
        .setFormat("properties")
        .setOptional(true)
        .setConfig(new JsonObject().put("path", "conf/run-config.properties"));
        //实例化 ConfigRetrieverOptions
        ConfigRetrieverOptions options = new ConfigRetrieverOptions()
        .addStore(fileStore1).addStore(fileStore2).addStore(fileStore3);

        // 实例化 ConfigRetriever
        ConfigRetriever retriever = ConfigRetriever.create(vertx, options);

        // 链式调用
        this.getConfig(retriever)
        .onSuccess(config -> {
            //生成 PgConnectOptions对象
            connectOptions = new PgConnectOptions()
            .setPort(config.getInteger("port"))
            .setHost(config.getString("host"))
            .setDatabase(config.getString("database"))
            .setUser(config.getString("user"))
            .setPassword(MyDesUtil.decrypt(config.getString("password"), "yulian@!"));
            JsonObject pg_poolOptions = config.getJsonObject("pg_poolOptions");
            //生成 PoolOptions对象
            poolOptions = new PoolOptions()
            .setMaxSize(pg_poolOptions.getInteger("maxSize"))
            .setIdleTimeout(pg_poolOptions.getInteger("idleTimeout"))
            .setMaxWaitQueueSize(pg_poolOptions.getInteger("maxWaitQueueSize"))
            .setPoolCleanerPeriod(pg_poolOptions.getInteger("poolCleanerPeriod"));

            //利用连接参数，连接池参数，实例化连接池对象
            pool = PgPool.pool(vertx, connectOptions, poolOptions);

            //生成vertx实例的WebClient对象
            WebClient webClient = WebClient.create(vertx);
            //生成vertx实例的事件总线对象 event bus
            EventBus eventBus = vertx.eventBus();
            //实例化Router
            router = Router.router(vertx);

            //获取body参数，得先添加这句
            router.route()
            //BodyHandler要放到第一个！！
            .handler(BodyHandler.create().setBodyLimit(100 * 1024 * 1024).setHandleFileUploads(true)
            //自定义文件上传到哪
            .setUploadsDirectory("webroot/upload"));
            //请求结束后是否删除上传文件
            //.setDeleteUploadedFilesOnEnd(true));
            //设置 webroot, static 会映射到webroot文件夹
            router.route("/static/*").handler(StaticHandler.create().setCachingEnabled(false).setDirectoryListing(false));
            //处理 CORS 请求
            router.route().handler(CorsHandler.create().addOrigin("*").allowedHeaders(allowedHeaders).allowedMethods(allowedMethods));

            /************
             *   vertx 授权管理   JWTAuth
             *******/
            //采用RSA256的加密文件方式来进行验证, 配置key，生成key.pem的方法
            //openssl rsa -in private.pem -outform PEM -pubout -out public.pem
            // read private key
            Buffer privateBuffer = vertx.fileSystem().readFileBlocking("RSA/private_key.pem");
            // read public key
            Buffer publicBuffer = vertx.fileSystem().readFileBlocking("RSA/public.pem");
            JWTAuthOptions jwtAuthOptions = new JWTAuthOptions()
            .addPubSecKey(new PubSecKeyOptions()
            .setAlgorithm("RS256")
            .setBuffer(publicBuffer)
            )
            .addPubSecKey(new PubSecKeyOptions()
            .setAlgorithm("RS256")
            .setBuffer(privateBuffer)
            );
            //JWTAuth对象 jwt, 通过 jwt 生成用户的token
            JWTAuth jwt = JWTAuth.create(vertx, jwtAuthOptions);
            //读取配置文件，是否需要后端进行数据访问权限控制
            int server_auth = Integer.parseInt(config.getString("SERVER_AUTH"));
            //web用户登录路由管理
            router.post("/web/signin").handler(req -> {
                JsonObject bodyJSObj = req.body().asJsonObject();
                UserManager userManager = new UserManager();
                userManager.webSignin(pool, jwt, bodyJSObj)
                .onSuccess(responseJSObj -> {
                    req.response()
                    .putHeader("content-type", "application/json")
                    .end(responseJSObj.toString());
                });
            });
            JWTAuthHandler authHandler = JWTAuthHandler.create(jwt);
            //配置所有的保护接口,  所有路径以 /web 或者 /api 开头的请求会被保护
            router.route("/web/*").handler(authHandler);
            router.route("/api/*").handler(authHandler);

            //批量导入excel中的数据
            router.route("/web/batch_import")
            .handler(req -> {
                //来自哪个页面的批量导入
                String source_from = req.request().getParam("source_from");
                BatchImport batchImport = new BatchImport();
                HashSet<FileUpload> fileUploadSet = new HashSet<FileUpload>(req.fileUploads());
                batchImport.handleUploadExcelFile(vertx, eventBus, pool, config, fileUploadSet, source_from, req.user())
                .onSuccess(responseJSObj -> {
                    req.response()
                    .putHeader("content-type", "application/json")
                    .end(responseJSObj.toString());
                });
            });
            //干线运费支付管理的路由
            router.post("/web/mainline_transport/*").handler(MinlineTransportHandler.create(pool, config, redis));
            //干线运费支付单的路由
            router.post("/web/mainline_freight_payment/*").handler(MainlineFreightPaymentHandler.create(pool, config,redis));
            //运输管理收入单的路由
            router.post("/web/transportation_revenue_code/*").handler(TransportRevenueHandler.create(pool, config,redis));
            //供应商批次商品进销存管理的路由
            router.post("/web/supplier_batch_goods_psi/*").handler(SupplierBatchGoodspsiHandler.create(pool, config,redis));
            //供应商批次商品成本斤价管理的路由
            router.post("/web/supplier_batch_goods_cost_price/*").handler(SupplierBatchGoodsCostPriceHandler.create(pool, config,redis));
            //商品毛利及提成管理的路由
            router.post("/web/goods_gross_profit_commission/*").handler(GoodsGrossProfitCommissionHandler.create(pool, config,redis));
            //商品毛利及提成管理明细的路由
            router.post("/web/goods_gross_profit_commission_detail/*").handler(GoodsGrossProfitCommissionDetailHandler.create(pool, config,redis));
            //供应商开票管理的路由
            router.post("/web/supplier_invoicing_management/*").handler(SupplierInvoicingManagementHandler.create(pool, config,redis));
            //品种商品产地和销售地价格趋势图
            router.post("/web/goods_price_trend/*").handler(GoodsPriceTrendHandler.create(pool, config,redis));
            //运输管理收入单
            router.post("/web/transport_revenue/*").handler(TransportRevenueHandler.create(pool, config,redis));
            //报表的conmon的路由
            router.post("/web/reportcommon/*").handler(ReportCommonHandler.create(pool, config));
            //conmon的路由
            router.post("/web/common/*").handler(CommonHandler.create(pool, config));

            //将Router与vertx HttpServer 绑定
            int port = config.getInteger("http.port");
            vertx.createHttpServer().requestHandler(router)
            .listen(port, http -> {
                if (http.succeeded()) {
                    //deployWorkers(1);
                    startPromise.complete();
                    logger.info("settlement-center server started on port {}", port);
                } else {
                    startPromise.fail(http.cause());
                }
            });
        })
        .onFailure(err -> {
            logger.info("获取配置文件错误: {}", err.getCause().getMessage());
        });
    }

    /**
     * 异步获取配置文件
     *
     * @param retriever
     * @return
     */
    private Future<JsonObject> getConfig(ConfigRetriever retriever) {
        Promise<JsonObject> promise = Promise.promise();
        retriever.getConfig(ar -> {
            if (ar.succeeded()) {
                JsonObject config = ar.result();
                promise.complete(config);
            } else {
                logger.info("Failed to retrieve the configuration, {} ", ar.cause().getMessage());
                promise.fail(ar.cause());
            }
        });
        return promise.future();
    }
}